package main;

import java.awt.Point;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Scanner;

import som.Algorithm;
import som.Board;
import som.Sample;
import view.GuiManager;

/**
 * Initializes system, trains the SOM and displays
 * the final clustering
 */
public class MainApp {

	private static final String OUTPUT_FILE_NAME = "output.txt";
	public static final int SAMPLE_LEN = 100; // length of each sample (10X10 image as vector with length 100)
	
	/**
	 * Parses train examples out of input file, trains SOM
	 * and displays final classification
	 * @param args input file name
	 * @throws FileNotFoundException if had an error creating output file
	 */
	public static void main(String[] args) throws FileNotFoundException {
		
		// parse input samples
		System.out.println("Reading input data");
		List<Sample> samples = parseSamples(args[0]);
		if (samples == null) {
			System.out.println("Error reading input samples- aborting!");
			return;
		}
		System.out.println("Done!");
		
		// initialize object to run the SOM
		System.out.println("Training the SOM and assigns the input samples to the final network...");
		Algorithm algo = new Algorithm(samples);
		Board resultBoard = algo.run();
		System.out.println("Done!");
		
		// display result
		GuiManager guiMngr = new GuiManager(resultBoard);
		guiMngr.display();
		
		// generate output file
		createOutputFile(resultBoard, samples.size());
	}

	/**
	 * Reads all train examples out of input file.
	 * Each example is stored as a vector of length 100
	 * instead of a 10X10 image
	 * @param inputFileName name of file holding input samples
	 * @return list holding each train example
	 */
	private static List<Sample> parseSamples(String inputFileName) {

		// initialize return value
		List<Sample> samples = new LinkedList<Sample>();
		
		int ctr = 0; // counts parsed examples
		
		// read each input matrix and store them as a vectors 
		try {
			
			String line;
			char[] lineArr;
			int valueIndex = 0; // current value in current sample
			
			// initialize vector for current sample
			double[] values = new double[SAMPLE_LEN];
			
			// read input file
			Scanner scan = new Scanner(new File(inputFileName));			
			while(scan.hasNext())
			{
				// parse current line
				line = scan.nextLine().trim();
				if(!line.equals(""))
				{
					lineArr = line.toCharArray();
					for(char c : lineArr)
					{
						try {
							values[valueIndex] = (c == '0') ? 0 : 1;
						} catch (Exception e) {
							e.printStackTrace();
						}
						valueIndex++;
					}
				}
				else // done reading current sample
				{
					samples.add(new Sample(values, (int) ctr / 10, ctr));
					++ctr;
					valueIndex = 0;
					values = new double[SAMPLE_LEN];
				}
			}
			
			// add last sample
			samples.add(new Sample(values, (int) ctr / 10, ctr));
			
			// close file
			scan.close();
			
			// return parsed samples
			return samples;
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * Creates an output file which displays the position of the best matching
	 * cell for each sample
	 * @param resultBoard trained SOM after assignment of train examples
	 * @param numSamples number of train examples
	 * @throws FileNotFoundException if had error creating output file
	 */
	private static void createOutputFile(Board resultBoard, int numSamples) throws FileNotFoundException {

		// create output file
		PrintWriter out = new PrintWriter(OUTPUT_FILE_NAME);
		out.println("#Sample\t Best Matching Cell Position");
		
		// initialize list mapping each sample index to its best matching cell's position
		List<Entry<Integer,Point>> samplesToCells = new ArrayList<Entry<Integer,Point>>(numSamples);		
		
		// for each cell
		for (int row = 0 ; row < Algorithm.BOARD_SIZE ; ++row) {
			for (int col = 0 ; col < Algorithm.BOARD_SIZE ; ++col) {
				
				// get current cell's assigned samples
				List<Sample> assignedSamples = resultBoard.getCell(row, col).getAssignedSamples();
				
				// map each sample to current cell's position
				for (Sample currSample : assignedSamples) {					
					samplesToCells
							.add(new AbstractMap.SimpleEntry<Integer, Point>(
									currSample.getIndex(), new Point(row, col)));
				}
			}
		}
		
		// sort samples by their index in input file
		Collections.sort(samplesToCells, new Comparator<Entry<Integer,Point>>() {

			public int compare(Entry<Integer,Point> first, Entry<Integer,Point> second) {

				Integer firstIndex = first.getKey();
				Integer secondIndex = second.getKey();
				
				return firstIndex.compareTo(secondIndex);
			}
		});
		
		// for each sample, write to file the position of its best matching cell
		for (Entry<Integer,Point> entry : samplesToCells) {
			out.println((entry.getKey() + 1) + " \t " + "("
					+ (entry.getValue().x + 1) + "," + (entry.getValue().y + 1)
					+ ")");
		}
		
		// close output file
		out.close();		
	}
}
